
namespace Behemoth.SharePoint.Data {

    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;

    using System.Web;
    using System.Web.Caching;

	public class DataSourceControlPropertiesCollection : Dictionary<string, DataSourceControlProperties> {

		public DataSourceControlPropertiesCollection() : base() { }

		public DataSourceControlProperties FindById( Guid id ) {
			// not very bloody effecient, but what can we do about it?
			foreach (DataSourceControlProperties dsp in this.Values) {
				FindDataSourceControlProperties finder = new FindDataSourceControlProperties( id );
				if (finder.DataSourceControlPropertiesPredicate( dsp )) {
					return dsp;
				}
			}
			return null;
		}
		public DataSourceControlProperties FindByName(string name) {
			DataSourceControlProperties dsp = null;
			if (this.ContainsKey( name ))
				this.TryGetValue( name, out dsp );
			return dsp;
		}

		public string AddFromFile(string filePath) {
			DataSourceControlProperties dsp = DataSourceControlProperties.GetPropertiesFromFile( filePath );
			this.Add( dsp.Name, dsp );
			return dsp.Name;
		}

		public void FillPropertiesCollectionFromFolder( string filePath ) {
			DirectoryInfo di = new DirectoryInfo( filePath );
			FileInfo[] fiArray = di.GetFiles( "*.xml" );
			foreach (FileInfo fi in fiArray) {
				DataSourceControlProperties dsp = null;
				try {
					dsp = DataSourceControlProperties.GetPropertiesFromFile( fi.FullName );
				} catch (Exception ex) {
					throw new Exception( string.Format( "The DataSourceControlProperties definition file could not be read or deserialized for file '{0}'. Error was: ", fi.FullName, ex.Message ), ex );
				}
				if (dsp != null) {
					if (this.ContainsKey( dsp.Name ))
						throw new ArgumentException( string.Format( "The DataSourceControlPropertiesCollection already contains a definition with the name '{0}'.", dsp.Name ) );
					this.Add( dsp.Name, dsp );
				}
			}
		}

		private const string DefaultDspFolder = @"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\DataSourceProperties";
		private const string DefaultWebCacheKey = "Behemoth.SharePoint.Data.DataSourceControlPropertiesCollection";

		/// <summary>
		/// Static property that creates and gets an instance of DataSourceControlPropertiesCollection
		/// populated by the default folder and persisted in System.Web.Cache.
		/// </summary>
		public static DataSourceControlPropertiesCollection PropertiesCache {
			get {
				Cache webCache = HttpContext.Current.Cache;
				DataSourceControlPropertiesCollection dspc = null;
				if (webCache == null || webCache[DefaultWebCacheKey] == null) {
					dspc = new DataSourceControlPropertiesCollection();
					dspc.FillPropertiesCollectionFromFolder( DefaultDspFolder );
					if (webCache != null)
						webCache.Add( DefaultWebCacheKey, dspc, null, DateTime.Now.AddHours( 12 ), Cache.NoSlidingExpiration, CacheItemPriority.AboveNormal, null ); 
				} else {
					dspc = webCache[DefaultWebCacheKey] as DataSourceControlPropertiesCollection;
				}
				return dspc;
			}
		}

		private const string FilePrefix = "file:///";

		/// <summary>
		/// Attempts to load a DSP object from multiple possible sources using a single
		/// configurable path string. Throws an exception if the load operation fails.
		/// </summary>
		/// <param name="path">A file path like 'file:///', or a name or Id (guid as string) from the cache collection</param>
		/// <returns></returns>
		public static DataSourceControlProperties Load( string path ) {
			DataSourceControlProperties dsp = null;
			if (path.StartsWith( FilePrefix )) {
				path = path.Substring( FilePrefix.Length );
				dsp = DataSourceControlProperties.GetPropertiesFromFile( path );
			} else if (DataSourceControlPropertiesCollection.PropertiesCache.ContainsKey( path )) {
				dsp = DataSourceControlPropertiesCollection.PropertiesCache.FindByName( path );
				// TODO implement a url based stoage/retreival method also
				// but... note that until now was have not relied on SharePoint API to get our information
			} else {
				// attempts to find it by its ID - very rare
				Guid id = Guid.Empty;
				try { id = new Guid( path ); } catch { id = Guid.Empty; }
				if (id != Guid.Empty)
					dsp = DataSourceControlPropertiesCollection.PropertiesCache.FindById( id );
				// TODO make a StringTools.IsGuid or TryParse method
			}
			// a last ditch attempt at backward compatibility
			if (dsp == null && System.IO.File.Exists( path ))
				dsp = DataSourceControlProperties.GetPropertiesFromFile( path );
			if (dsp == null)
				throw new IndexOutOfRangeException( string.Format( "The item with the specified Name or Id, '{0}', was not found in DataSourceControlPropertiesCollection.PropertiesCache.", path ) );
			return dsp;
		}

	} // class

	public class FindDataSourceControlProperties {

		private string name;
		private Guid id;

		public FindDataSourceControlProperties( string name ) {
			this.name = name;
		}
		public FindDataSourceControlProperties( Guid id ) {
			this.id = id;
		}

		public bool DataSourceControlPropertiesPredicate( DataSourceControlProperties target ) {
			return ((this.id != Guid.Empty && target.ID_Guid == this.id)
				|| (!string.IsNullOrEmpty(this.name) && target.Name == this.name));
		}

	}

}
